package com.inepex.inechart.shared.linechart;

import java.util.ArrayList;

import com.inepex.inechart.client.Defaults;

public class PointFilter {

	public enum Policy{
		lower,
		higher,
		average
	}

	protected double horizontalFilter, verticalFilter;
	protected Policy policy;
	protected LineChartModelAssist lineChartModelAssist;

	public PointFilter() {
		this(Defaults.hotizontalFilter, Defaults.verticalFilter, Defaults.filterPolicy);
	}

	public PointFilter(double horizontalFilter, double verticalFilter, Policy policy) {
		this.horizontalFilter = horizontalFilter;
		this.verticalFilter = verticalFilter;
		this.policy = policy;
	}

	public ArrayList<Point> filterDataPoints(ArrayList<Point> unfiltered){

		if(horizontalFilter <= 0 && verticalFilter <= 0 || unfiltered.size() < 2){
			return unfiltered;
		}
		else{
			ArrayList<Point> filtered = new ArrayList<Point>();
			ArrayList<Point> overlapping = null;
			Point last = null;
			for(Point actual : unfiltered){
				if(last == null){
					last = actual;
					continue;
				}
				if(areOverlappingPoints(last, actual)){
					if(overlapping == null){
						overlapping = new ArrayList<Point>();
						overlapping.add(last);
					}
					overlapping.add(actual);
				}
				else{
					if(overlapping == null) {
						filtered.add(last);
					}
					else{
						filtered.add(applyFilterPolicy(overlapping));
						overlapping = null;
					}
					last = actual;
				}
			}
			if(overlapping != null) {
				filtered.add(applyFilterPolicy(overlapping));
			}
			else if(last != null){
				filtered.add(last);
			}
			return filtered;
		}	
	}

	protected Point applyFilterPolicy(ArrayList<Point> dps){
		double[] firstVP = dps.get(0).getUnderLyingData();
		double[] lastVP = dps.get(dps.size()-1).getUnderLyingData();
		double minX = firstVP[0], minY = Double.MAX_VALUE, maxX = lastVP[0], maxY = - Double.MAX_VALUE;
		Point minYdp = null, maxYdp = null;
		for(Point dp : dps){
			double[] vp = dp.getUnderLyingData();
			if(minY > vp[1]){
				minY = vp[1];
				minYdp = dp;
			}
			if(maxY < vp[1]){
				maxY = vp[1];
				maxYdp = dp;
			}

		}
		Point ret;
		switch (policy) {
		case average:
			ret = new Point(minX + (maxX - minX) / 2, minY + (maxY - minY) / 2);
			ret.bestMatchingFilteredPoint = dps.get(0);
			break;
		case lower:
			ret = new Point(minX + (maxX - minX) / 2, minY);
			ret.bestMatchingFilteredPoint = minYdp;
			break;
		case higher:
		default:
			ret = new Point(minX + (maxX - minX) / 2, maxY);
			ret.bestMatchingFilteredPoint = maxYdp;
			break;
		}	
		lineChartModelAssist.setDataPoint(ret);
		ret.filteredPoints = dps;
		return ret;
	}

	protected boolean areOverlappingPoints(Point first, Point second){
		if(first.unfilterable || second.unfilterable){
			return false;
		}
		else if( horizontalFilter > 0 && Math.abs(first.canvasX - second.canvasX) <= horizontalFilter || verticalFilter > 0 && Math.abs(first.canvasY - second.canvasY) <= verticalFilter ){
			return true;
		}
		else return false;
	}

	protected void setLineChartModelAssist(LineChartModelAssist lineChartModelAssist){
		this.lineChartModelAssist = lineChartModelAssist;
	}

	public double getHorizontalFilter() {
		return horizontalFilter;
	}

	public void setHorizontalFilter(double horizontalFilter) {
		this.horizontalFilter = horizontalFilter;
	}

	public double getVerticalFilter() {
		return verticalFilter;
	}

	public void setVerticalFilter(double verticalFilter) {
		this.verticalFilter = verticalFilter;
	}

	public Policy getPolicy() {
		return policy;
	}

	public void setPolicy(Policy policy) {
		this.policy = policy;
	}

}
